package com.quectel.cms.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ReflectUtil;
import com.google.common.base.Preconditions;
import com.quectel.cms.interceptor.LoginUserSession;
import com.quectel.cms.util.SessionHolder;
import com.quectel.constant.global.CommonField;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.sys.SysConstants;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.device.service.DeviceTypeService;
import com.quectel.core.module.group.dto.SysGroupVillageDto;
import com.quectel.core.module.group.service.SysGroupDeviceCategoryService;
import com.quectel.core.module.group.service.SysGroupDeviceTypeService;
import com.quectel.core.module.group.service.SysGroupVillageService;
import com.quectel.core.module.group.service.SysRegionVillageRefService;
import com.quectel.core.module.resource.service.ResourceGroupService;
import com.quectel.sys.module.sys.dto.SysGroupDto;
import com.quectel.sys.module.sys.service.SysGroupService;
import com.quectel.sys.module.sys.service.SysMenuService;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.Request;
import com.quectel.util.common.Response;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.quectel.constant.global.StatisticsConstants.*;

public class BaseController {
    @Autowired
    protected HttpServletRequest httpServletRequest;
    @DubboReference
    private SysRegionVillageRefService sysRegionVillageRefService;
    @DubboReference
    private SysGroupService sysGroupService;
    @DubboReference
    private SysGroupDeviceCategoryService sysGroupDeviceCategoryService;
    @DubboReference
    private SysGroupDeviceTypeService sysGroupDeviceTypeService;
    @DubboReference
    private SysMenuService sysMenuService;
    @DubboReference
    private SysGroupVillageService sysGroupVillageService;
    @DubboReference
    private ResourceGroupService resourceGroupService;
    @DubboReference
    private DeviceService deviceService;
    @DubboReference
    private DeviceTypeService deviceTypeService;

    final static private Byte AUTH_REGION_TYPE_CODE = 4;
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Response<Object> handleException(Exception e) {
        logger.error("BaseController报警", e);
        if (e instanceof MethodArgumentNotValidException) {
            //post参数校验出错
            BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            //咱们找其中之一就可以
            if (bindingResult.getFieldError() != null) {
                return Response.error("数据校验不通过");
            }
            return Response.error("校验错误");
        }
        if (e instanceof ConstraintViolationException) {
            //get参数出错
            return Response.error(e.getLocalizedMessage());
        }
        if (e instanceof MethodArgumentTypeMismatchException) {
            //get 参数类型不匹配
            return Response.error("非法的查询参数");
        }
        if (e instanceof RpcException) {
            //dubbo服务者不可用
            return Response.error("服务正在重启，请稍后");
        }

        return Response.customize(500, "服务器好像出了点小问题");
    }

    /**
     * 填充内存条件基于小区级的权限 (跨区授权)
     *
     * @param params
     * @return
     */
    public void fillAuthorityDataByRegionOrVillageInMemory(Map<String, Object> params) {
        LoginUserSession sysUser = SessionHolder.getSysUser();
        Preconditions.checkNotNull(sysUser);


        // 租户过滤
        params.put(LambdaUtil.getFieldName(SysGroupDto::getTenantId), sysUser.getTenantId());

        if (SysConstants.SYS_TOP_GROUP_CODE.equals(sysUser.getGroupCode())) {
            // 如果是顶级租户直接返回即可
            return;
        }
        SysGroupDto sysGroupDto = sysGroupService.selectCacheById(sysUser.getGroupId());
        Preconditions.checkNotNull(sysGroupDto);

        if (SysConstants.GroupAuthEnum.BY_VILLAGE.getType().equals(sysGroupDto.getAuthorityType())) {
            // 小区授权
            List<SysGroupVillageDto> sysGroupVillageDtoList = sysGroupVillageService.queryListByGroupId(sysGroupDto.getId());
            params.put("villageIds", sysGroupVillageDtoList.stream().map(SysGroupVillageDto::getVillageId).collect(Collectors.toList()));
        } else if (SysConstants.GroupAuthEnum.BY_REGION.getType().equals(sysGroupDto.getAuthorityType())) {
            // 社区授权
            // 翻译成小区
            sysRegionVillageRefService.executeRef(sysGroupDto.getId());

            List<SysGroupVillageDto> sysGroupVillageDtoList = sysGroupVillageService.queryListByGroupId(sysGroupDto.getId());
            params.put("villageIds", sysGroupVillageDtoList.stream().map(SysGroupVillageDto::getVillageId).collect(Collectors.toList()));
        } else {
            //查询越界 强制查询结果集为空
            params.put(SystemConstants.ENTITY_ID_FLAG, -1L);
        }

    }

    /**
     * 填充授权数据
     *
     * @return
     */
    protected Map<String, Object> fillAuthorityData(Map<String, Object> params) {
        LoginUserSession loginUser = SessionHolder.getSysUser();

        SysGroupDto sysGroupDto = sysGroupService.selectCacheById(loginUser.getGroupId());

        params.put("_sysGroup", BeanUtil.beanToMap(sysGroupDto));

        fillTenantData(params);

        // 懒加载区域权限
        Byte authorityType = sysGroupDto.getAuthorityType();
        if (!SysConstants.SYS_TOP_GROUP_CODE.equals(sysGroupDto.getCode())
                && AUTH_REGION_TYPE_CODE.equals(authorityType)) {
            sysRegionVillageRefService.executeRef(sysGroupDto.getId());
        }
        return params;
    }

    /**
     * 填充资源库资源编码
     *
     * @param params 请求参数
     * @param code   资源库编码
     * @return
     */
    protected Map<String, Object> fillResourceGroupCode(Map<String, Object> params, String code) {
        params.put("_resourceGroupCode", code);
        return params;
    }

    /**
     * 填充租户数据
     *
     * @param params
     */
    protected void fillTenantData(Map<String, Object> params) {
        LoginUserSession loginUser = SessionHolder.getSysUser();

        loginUser.setPermissions(null);
        loginUser.setUserRoles(null);

        params.put("_loginUserSession", BeanUtil.beanToMap(loginUser));
    }


    /**
     * 是否拥有对设备的管理权限
     *
     * @param imei
     * @return
     */
    protected boolean hasDevicePrivileges(String imei) {

        Map<String, Object> params = new HashMap<>();
        params.put("imei", imei);
        Request.configParams(params).configPage(1, 1);

        this.fillAuthorityData(params);

        //查询这个人是否管理了这个设备
        List<DeviceDto> deviceDtoList = deviceService.queryList(params);
        if (deviceDtoList == null || deviceDtoList.size() <= 0) {
            return false;
        }
        return true;
    }


    /**
     * 填充保存时的公共参数
     *
     * @param obj
     */
    protected void fillSaveCommonData(Object obj) {
        LoginUserSession loginUser = SessionHolder.getSysUser();
        if (obj != null && loginUser != null) {
            Field partyField = ReflectUtil.getField(obj.getClass(), CommonField.PARTY_ID);
            if (partyField != null) {
                ReflectUtil.setFieldValue(obj, CommonField.PARTY_ID, loginUser.getPartyId());
            }
            Field tenantField = ReflectUtil.getField(obj.getClass(), CommonField.TENANT_ID);
            if (tenantField != null) {
                ReflectUtil.setFieldValue(obj, CommonField.TENANT_ID, loginUser.getTenantId());
            }
            Field createField = ReflectUtil.getField(obj.getClass(), CommonField.CREATOR);
            if (createField != null) {
                ReflectUtil.setFieldValue(obj, CommonField.CREATOR, loginUser.getAccount());
            }

            Field createTimeField = ReflectUtil.getField(obj.getClass(), CommonField.CREATE_TIME);
            if (createTimeField != null) {
                ReflectUtil.setFieldValue(obj, CommonField.CREATE_TIME, new Date());
            }
            Field editorField = ReflectUtil.getField(obj.getClass(), CommonField.EDITOR);
            if (editorField != null) {
                //设置空(防止被前端注入时间)
                ReflectUtil.setFieldValue(obj, CommonField.EDITOR, null);
            }
            Field editTimeField = ReflectUtil.getField(obj.getClass(), CommonField.EDIT_TIME);
            if (editTimeField != null) {
                //设置空(防止被前端注入时间)
                ReflectUtil.setFieldValue(obj, CommonField.EDIT_TIME, null);
            }


        }
    }

    /**
     * 填充编辑时的公共参数
     *
     * @param obj
     */
    protected void fillEditCommonData(Object obj) {
        LoginUserSession loginUser = SessionHolder.getSysUser();
        if (obj != null && loginUser != null) {

            Field createField = ReflectUtil.getField(obj.getClass(), CommonField.CREATOR);
            if (createField != null) {
                //设置空不更新(防止被前端注入时间) 如果调用updateAllColumnById则需要自己重新设置 防止把数据库更新为null
                ReflectUtil.setFieldValue(obj, CommonField.CREATOR, null);
            }

            Field createTimeField = ReflectUtil.getField(obj.getClass(), CommonField.CREATE_TIME);
            if (createTimeField != null) {
                //设置空不更新(防止被前端注入时间) 如果调用updateAllColumnById则需要自己重新设置 防止把数据库更新为null
                ReflectUtil.setFieldValue(obj, CommonField.CREATE_TIME, null);
            }
            Field editorField = ReflectUtil.getField(obj.getClass(), CommonField.EDITOR);
            if (editorField != null) {

                ReflectUtil.setFieldValue(obj, CommonField.EDITOR, loginUser.getAccount());
            }
            Field editTimeField = ReflectUtil.getField(obj.getClass(), CommonField.EDIT_TIME);
            if (editTimeField != null) {
                ReflectUtil.setFieldValue(obj, CommonField.EDIT_TIME, new Date());
            }

        }
    }


    /**
     * 检验时间范围
     *
     * @param unitType  DAY,MONTH,HOUR 默认为DAY
     * @param startTime 包含yyyy-MM-dd HH:mm:ss
     * @param endTime   包含yyyy-MM-dd HH:mm:ss
     * @param limit     显示时间根据事件单位传入即可 null默认各个单位下的默认值 HOUR 24 DAY 14  YEAR 1
     * @return
     */
    public Response<Object> checkTimeRange(String unitType, String startTime, String endTime, Integer limit) {
        if (unitType == null) {
            unitType = TIME_RANGE_BY_DAY;
        }
        if (TIME_RANGE_BY_HOUR.equals(unitType)) {
            Date startDate = DateUtils.parse(startTime, DateUtils.FormatType.COMMON);
            Date endDate = DateUtils.parse(endTime, DateUtils.FormatType.COMMON);
            int l = (limit == null ? DEFAULT_HOUR_LIMIT : limit);
            long betweenHour = DateUtil.between(DateUtil.beginOfHour(startDate), DateUtil.endOfHour(endDate), DateUnit.HOUR, true);
            if (betweenHour > l) {
                return Response.error("日期超出最大限定，最大范围:" + l + "小时");
            }
        }
        if (TIME_RANGE_BY_DAY.equals(unitType)) {
            Date startDate = DateUtils.parse(startTime, DateUtils.FormatType.SIMPLE);
            Date endDate = DateUtils.parse(endTime, DateUtils.FormatType.SIMPLE);
            int l = (limit == null ? DEFAULT_DAY_LIMIT : limit);
            long betweenDay = DateUtil.betweenDay(DateUtil.beginOfDay(startDate), DateUtil.endOfDay(endDate), true);
            if (betweenDay > l) {
                return Response.error("日期超出最大限定，最大范围:" + l + "天");
            }
        }

        if (TIME_RANGE_BY_WEEK.equals(unitType)) {
            Date startDate = DateUtils.parse(startTime, DateUtils.FormatType.SIMPLE);
            Date endDate = DateUtils.parse(endTime, DateUtils.FormatType.SIMPLE);
            int l = (limit == null ? DEFAULT_WEEK_LIMIT : limit);
            long betweenDay = DateUtil.betweenWeek(DateUtil.beginOfDay(startDate), DateUtil.endOfDay(endDate), true);
            if (betweenDay > l) {
                return Response.error("日期超出最大限定，最大范围:" + l + "天");
            }
        }
        if (TIME_RANGE_BY_MONTH.equals(unitType)) {

            Date startDate = DateUtils.parse(startTime, DateUtils.FormatType.MONTH);
            Date endDate = DateUtils.parse(endTime, DateUtils.FormatType.MONTH);
            // 按月统计校验
            int l = (limit == null ? DEFAULT_MONTH_LIMIT : limit);
            long betweenMonth = DateUtil.betweenMonth(DateUtil.beginOfMonth(startDate), DateUtil.endOfMonth(endDate), true);
            if (betweenMonth > l) {
                return Response.error("日期超出最大限定，最大范围:" + l + "月");
            }
        }
        return Response.ok();
    }
}
